package com.d3works.apps.account.service.move;

import com.d3works.apps.account.db.FiscalPosition;
import com.d3works.apps.account.db.Invoice;
import com.d3works.apps.account.db.Journal;
import com.d3works.apps.account.db.Move;
import com.d3works.apps.account.db.MoveLine;
import com.d3works.apps.account.db.PaymentMode;
import com.d3works.apps.account.db.PaymentVoucher;
import com.d3works.apps.account.db.repo.MoveRepository;
import com.d3works.apps.account.service.app.AppAccountService;
import com.d3works.apps.base.db.BankDetails;
import com.d3works.apps.base.db.Company;
import com.d3works.apps.base.db.Currency;
import com.d3works.apps.base.db.Partner;
import com.d3works.apps.base.db.repo.YearRepository;
import com.d3works.apps.base.service.PeriodService;
import com.d3works.apps.base.service.administration.SequenceService;
import com.d3works.apps.base.service.config.CompanyConfigService;
import com.d3works.apps.tool.exception.ToolExceptionMessage;
import com.d3works.exception.D3worksException;
import com.d3works.exception.db.repo.TraceBackRepository;
import com.d3works.i18n.I18n;
import com.d3works.i18n.L10n;
import com.d3works.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.servlet.RequestScoped;
import java.lang.invoke.MethodHandles;
import java.time.LocalDate;
import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RequestScoped
public class MoveCreateServiceImpl implements MoveCreateService {

  private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  protected PeriodService periodService;
  protected MoveRepository moveRepository;
  protected CompanyConfigService companyConfigService;

  protected AppAccountService appAccountService;

  @Inject
  public MoveCreateServiceImpl(
      AppAccountService appAccountService,
      PeriodService periodService,
      MoveRepository moveRepository,
      CompanyConfigService companyConfigService) {

    this.periodService = periodService;
    this.moveRepository = moveRepository;
    this.companyConfigService = companyConfigService;

    this.appAccountService = appAccountService;
  }

  /**
   * Créer une écriture comptable à la date du jour impactant la compta.
   *
   * @param journal
   * @param period
   * @param company
   * @param invoice
   * @param partner
   * @param isReject <code>true = écriture de rejet avec séquence spécifique</code>
   * @return
   * @throws D3worksException
   */
  @Override
  public Move createMove(
      Journal journal,
      Company company,
      Currency currency,
      Partner partner,
      PaymentMode paymentMode,
      FiscalPosition fiscalPosition,
      int technicalOriginSelect,
      int functionalOriginSelect,
      String origin,
      String description,
      BankDetails companyBankDetails)
      throws D3worksException {
    return this.createMove(
        journal,
        company,
        currency,
        partner,
        appAccountService.getTodayDate(company),
        null,
        paymentMode,
        fiscalPosition,
        technicalOriginSelect,
        functionalOriginSelect,
        origin,
        description,
        companyBankDetails);
  }

  @Override
  public Move createMove(
      Journal journal,
      Company company,
      Currency currency,
      Partner partner,
      LocalDate date,
      LocalDate originDate,
      PaymentMode paymentMode,
      FiscalPosition fiscalPosition,
      BankDetails bankDetails,
      int technicalOriginSelect,
      int functionalOriginSelect,
      String origin,
      String description,
      BankDetails companyBankDetails)
      throws D3worksException {
    return this.createMove(
        journal,
        company,
        currency,
        partner,
        date,
        originDate,
        paymentMode,
        fiscalPosition,
        bankDetails,
        technicalOriginSelect,
        functionalOriginSelect,
        false,
        false,
        false,
        origin,
        description,
        companyBankDetails);
  }

  /**
   * create an account move
   *
   * @param journal
   * @param company
   * @param currency
   * @param partner
   * @param date
   * @param paymentMode
   * @param fiscalPosition
   * @param technicalOriginSelect
   * @return
   * @throws D3worksException
   */
  @Override
  public Move createMove(
      Journal journal,
      Company company,
      Currency currency,
      Partner partner,
      LocalDate date,
      LocalDate originDate,
      PaymentMode paymentMode,
      FiscalPosition fiscalPosition,
      int technicalOriginSelect,
      int functionalOriginSelect,
      String origin,
      String description,
      BankDetails companyBankDetails)
      throws D3worksException {
    return this.createMove(
        journal,
        company,
        currency,
        partner,
        date,
        originDate,
        paymentMode,
        fiscalPosition,
        null,
        technicalOriginSelect,
        functionalOriginSelect,
        false,
        false,
        false,
        origin,
        description,
        companyBankDetails);
  }

  /**
   * Creating a new generic accounting move
   *
   * @param journal
   * @param company
   * @param currency
   * @param partner
   * @param date
   * @param paymentMode
   * @param fiscalPosition
   * @param technicalOriginSelect
   * @param ignoreInDebtRecoveryOk
   * @param ignoreInAccountingOk
   * @return
   * @throws D3worksException
   */
  @Override
  public Move createMove(
      Journal journal,
      Company company,
      Currency currency,
      Partner partner,
      LocalDate date,
      LocalDate originDate,
      PaymentMode paymentMode,
      FiscalPosition fiscalPosition,
      BankDetails bankDetails,
      int technicalOriginSelect,
      int functionalOriginSelect,
      boolean ignoreInDebtRecoveryOk,
      boolean ignoreInAccountingOk,
      boolean autoYearClosureMove,
      String origin,
      String description,
      BankDetails companyBankDetails)
      throws D3worksException {
    log.debug(
        "Creating a new generic accounting move (journal : {}, company : {}",
        new Object[] {journal.getName(), company.getName()});

    Move move = new Move();

    move.setJournal(journal);
    move.setCompany(company);

    move.setIgnoreInDebtRecoveryOk(ignoreInDebtRecoveryOk);
    move.setIgnoreInAccountingOk(ignoreInAccountingOk);
    move.setAutoYearClosureMove(autoYearClosureMove);

    if (autoYearClosureMove) {
      move.setPeriod(periodService.getPeriod(date, company, YearRepository.TYPE_FISCAL));
      if (move.getPeriod() == null) {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(ToolExceptionMessage.PERIOD_1),
            company.getName(),
            L10n.getInstance().format(date));
      }
    } else {
      move.setPeriod(periodService.getActivePeriod(date, company, YearRepository.TYPE_FISCAL));
    }

    move.setDate(date);
    move.setOriginDate(originDate);
    move.setMoveLineList(new ArrayList<MoveLine>());

    Currency companyCurrency = companyConfigService.getCompanyCurrency(company);

    if (companyCurrency != null) {
      move.setCompanyCurrency(companyCurrency);
      move.setCompanyCurrencyCode(companyCurrency.getCodeISO());
    }

    if (currency == null) {
      currency = move.getCompanyCurrency();
    }
    if (currency != null) {
      move.setCurrency(currency);
      move.setCurrencyCode(currency.getCodeISO());
    }
    move.setOrigin(origin);
    move.setDescription(description);
    move.setPartner(partner);
    move.setPaymentMode(paymentMode);
    move.setFiscalPosition(fiscalPosition);
    move.setPartnerBankDetails(bankDetails);
    move.setCompanyBankDetails(companyBankDetails);
    move.setTechnicalOriginSelect(technicalOriginSelect);
    move.setFunctionalOriginSelect(functionalOriginSelect);
    moveRepository.save(move);
    move.setReference(Beans.get(SequenceService.class).getDraftSequenceNumber(move));

    return move;
  }

  /**
   * Créer une écriture comptable de toute pièce spécifique à une saisie paiement.
   *
   * @param journal
   * @param period
   * @param company
   * @param invoice
   * @param partner
   * @param isReject <code>true = écriture de rejet avec séquence spécifique</code>
   * @param agency L'agence dans laquelle s'effectue le paiement
   * @return
   * @throws D3worksException
   */
  @Override
  public Move createMoveWithPaymentVoucher(
      Journal journal,
      Company company,
      PaymentVoucher paymentVoucher,
      Partner partner,
      LocalDate date,
      PaymentMode paymentMode,
      FiscalPosition fiscalPosition,
      int technicalOriginSelect,
      int functionalOriginSelect,
      String origin,
      String description,
      BankDetails companyBankDetails)
      throws D3worksException {
    Move move =
        this.createMove(
            journal,
            company,
            paymentVoucher.getCurrency(),
            partner,
            date,
            null,
            paymentMode,
            fiscalPosition,
            technicalOriginSelect,
            functionalOriginSelect,
            origin,
            description,
            companyBankDetails);
    move.setPaymentVoucher(paymentVoucher);
    return move;
  }

  @Override
  public Move createMove(
      Journal journal,
      Company company,
      Currency currency,
      Partner partner,
      LocalDate date,
      PaymentMode paymentMode,
      FiscalPosition fiscalPosition,
      int technicalOriginSelect,
      int functionalOriginSelect,
      boolean ignoreInDebtRecoveryOk,
      boolean ignoreInAccountingOk,
      boolean autoYearClosureMove,
      String origin,
      String description,
      Invoice invoice,
      PaymentVoucher paymentVoucher,
      BankDetails companyBankDetails)
      throws D3worksException {
    Move move =
        this.createMove(
            journal,
            company,
            currency,
            partner,
            date,
            null,
            paymentMode,
            fiscalPosition,
            null,
            technicalOriginSelect,
            functionalOriginSelect,
            ignoreInDebtRecoveryOk,
            ignoreInAccountingOk,
            autoYearClosureMove,
            origin,
            description,
            companyBankDetails);
    move.setInvoice(invoice);
    move.setPaymentVoucher(paymentVoucher);
    return move;
  }
}
